/*
 * Su-yun Information Technology, Co.,Ltd CONFIDENTIAL
 *
 * (C) Copyright Su-yun Information Technology, Co.,Ltd. 2021 All Rights Reserved.
 *
 * NOTICE:  All information contained herein is, and remains the property of
 * Su-yun Information Technology, Co.,Ltd. The intellectual property herein
 * are protected by trade secret, patent law or copyright law. Dissemination
 * of this information or reproduction of this material is strictly forbidden
 * unless prior written permission is obtained from Su-yun Information Technology.
 *
 * https://suyunplat.com/
 *
 */
package com.xtiim.battery.discharge.protocal;

import com.google.common.collect.Lists;
import com.xtiim.battery.discharge.protocal.manager.CommunicationManager;
import com.xtiim.battery.discharge.util.ByteUtil;
import com.xtiim.battery.discharge.util.TimeUtil;
import lombok.extern.slf4j.Slf4j;

import java.util.List;

/**
 * Protocol response reader
 *
 */
@Slf4j
public class ResponseReader {

    public static final byte COMM_RESPONSE = 0x05;

    private static final byte RESPONSE_COLLECT_INSPECT_FLAG = 0x19;
    private static final byte RESPONSE_COLLECT_STATUS_FLAG = 0x29;

    /**
     * Len of internal [record], e.g.
     * EB len 00 19 [record] EC xor
     *
     */
    public static final byte INSPECT_RECORD_ITEM_LEN = 49;

    /**
     * Len of volt in one line
     */
    private static final byte STATUS_RECORD_VOLT_IN_LINE_LEN = 40;

    /**
     * Answer whether the response is 0x05
     *
     * @param bytes
     * @return
     */
    public boolean isCommonResponse(byte[] bytes) {
        return bytes.length == 1 && bytes[0] == COMM_RESPONSE;
    }

    /**
     * Answer the response type for given data
     *
     * @param bytes
     * @return
     */
    public ResponseType responseType(byte[] bytes) {
        if (bytes.length == 1 && bytes[0] == COMM_RESPONSE) {
            return ResponseType.COMMON_RESPONSE;
        }

        if (bytes[0] == CommunicationManager.EB && bytes[3] == RESPONSE_COLLECT_INSPECT_FLAG) {
            return ResponseType.INSPECT_DATA;
        }

        if (bytes[0] == CommunicationManager.EB && bytes[3] == RESPONSE_COLLECT_STATUS_FLAG) {
            return ResponseType.STATUS_DATA;
        }

        return ResponseType.NONE;
    }

    /**
     * Read inspect record list
     *
     * @param bytes
     * @return
     */
    public List<InspectRecord> readInspectRecordList(byte[] bytes) {
        if (!validateInspectRecordContentLen(bytes)) {
            return null;
        }

        List<InspectRecord> records = Lists.newArrayList();
        int startIndex = 4;

        while ((bytes.length - startIndex) > INSPECT_RECORD_ITEM_LEN) {
            InspectRecord inspectRecord = readInspectRecord(bytes, startIndex);
            startIndex += INSPECT_RECORD_ITEM_LEN;
            records.add(inspectRecord);
        }

        return records;
    }

    /**
     * Read inspect record
     *
     * |EB|len|0|19|0|v_type|line|y|y|m|d|h|m|s|v..v|ED|xor|
     *
     *
     * @param bytes
     * @param startIndex
     * @return
     */
    private InspectRecord readInspectRecord(byte[] bytes, int startIndex) {
        Object[] items = readBatteryData(bytes, startIndex);

        InspectRecord record = new InspectRecord.BatteryDataRecord(Byte.toUnsignedInt((byte) items[0]),
                Byte.toUnsignedInt((byte) items[1]),
                TimeUtil.bytesToDate((byte[]) items[2]),
                (double[]) items[3]);

        log.debug("[DataRecord] " + record);
        return record;
    }

    /**
     * Read status record
     *
     * status record format:
     * |EA|len|19|host|04|step|state|I|V|V1|V2|V3|V4|V5|V6|T1|T2|T3|T4|T5|T6|EC|xor|
     *
     * @param bytes
     * @return
     */
    public StatusRecord readStatusRecord(byte[] bytes) {
        if (!validateStatusRecordContentLen(bytes)) {
            return null;
        }

        int index = 4;
        double[] volts = new double[20];
        for (int i = 0; i < 20; i++) {
            volts[i] = ByteUtil.bcd2Double(bytes[index], bytes[index + 1], 2, false);
            index += 2;
        }

        int line = Byte.toUnsignedInt(bytes[2]) + 1; //line from 0-5
        StatusRecord record = new StatusRecord(line, volts);

        log.debug("[StatusRecord] " + record);
        return record;
    }

    /**
     * Validate status record content length
     *
     * @param bytes
     * @return
     */
    private boolean validateStatusRecordContentLen(byte[] bytes) {
        int len = Byte.toUnsignedInt(bytes[1]);
        if (((len - 3) != STATUS_RECORD_VOLT_IN_LINE_LEN)) {
            log.error("Invalid status content length.");
            return false;
        }
        return true;
    }

    /**
     * Validate data record content length
     *
     * @param bytes
     * @return
     */
    private boolean validateInspectRecordContentLen(byte[] bytes) {
        int len = Byte.toUnsignedInt(bytes[1]);
        if (!((len - 3) == INSPECT_RECORD_ITEM_LEN)) {
            log.error("Invalid data record length.");
            return false;
        }
        return true;
    }

    /**
     * Read battery data from the input bytes
     *
     * @param bytes
     * @param index
     * @return
     */
    private Object[] readBatteryData(byte[] bytes, int index) {
        Object[] items = new Object[4];

        int curIndex = index;

        // skip 1 padding. read volt type
        items[0] = ByteUtil.bcd2Dec(bytes[curIndex + 1]);
        curIndex += 2;

        // line
        items[1] = ByteUtil.bcd2Dec(bytes[curIndex]);
        curIndex += 1;

        // date
        byte[] dateBytes = new byte[6];
        for (int i = 0; i < 6; i++) {
            dateBytes[i] = ByteUtil.bcd2Dec(bytes[curIndex + i]);
        }
        items[2] = dateBytes;
        curIndex += 6;

        // volts
        double[] volts = new double[20];
        for (int i = 0; i < 20; i++) {
            volts[i] = ByteUtil.bcd2Double(bytes[curIndex], bytes[curIndex + 1], 2, false);
            curIndex += 2;
        }
        items[3] = volts;

        return items;
    }

    public static void main(String[] args) {

        System.out.println(RESPONSE_COLLECT_STATUS_FLAG);

        System.out.println(CommunicationManager.EB);

        Integer integer = new Integer(0xEB);
        System.out.println(integer);
        System.out.println(integer.byteValue());

    }
}
